#include "DynLib.h"

#if _WIN32
#    define DYNLIB_LOAD( a ) LoadLibraryEx( a, NULL, 0 ) // we can not use LOAD_WITH_ALTERED_SEARCH_PATH with relative paths
#    define DYNLIB_GETSYM( a, b ) GetProcAddress( a, b )
#    define DYNLIB_UNLOAD( a ) !FreeLibrary( a )
#else
#    define DYNLIB_LOAD( a ) dlopen( a, RTLD_LAZY | RTLD_GLOBAL)
#    define DYNLIB_GETSYM( a, b ) dlsym( a, b )
#    define DYNLIB_UNLOAD( a ) dlclose( a )
#endif

#if _WIN32
#  define WIN32_LEAN_AND_MEAN
#  if !defined(NOMINMAX) && defined(_MSC_VER)
#   define NOMINMAX // required to stop windows.h messing up std::min
#  endif
#  include <windows.h>
#else
extern "C" {
#   include <dlfcn.h>
}
#endif

namespace OCPP {

    //-----------------------------------------------------------------------
    DynLib::DynLib( const String& name )
    {
        mName = name;
        mInst = NULL;
    }

    //-----------------------------------------------------------------------
    DynLib::~DynLib()
    {
    }

    //-----------------------------------------------------------------------
    void DynLib::load()
    {
        String name = mName;
#if _WIN32
        // Although LoadLibraryEx will add .dll itself when you only specify the library name,
 // if you include a relative path then it does not. So, add it to be sure.
        if (name.substr(name.find_last_of(".") + 1) != "dll")
            name += ".dll";
#else
        // dlopen() does not add .so to the filename, like windows does for .dll
        if (name.find(".so") == String::npos)
        {
            name += ".so";
        }
#endif

        mInst = (DYNLIB_HANDLE)DYNLIB_LOAD( name.c_str() );
    }

    //-----------------------------------------------------------------------
    void DynLib::unload()
    {
        // Log library unload
        if( DYNLIB_UNLOAD( mInst ) )
        {
            
        }

    }

    //-----------------------------------------------------------------------
    void* DynLib::getSymbol( const String& strName ) const throw()
    {
        return (void*)DYNLIB_GETSYM( mInst, strName.c_str() );
    }
    //-----------------------------------------------------------------------
    String DynLib::dynlibError( void ) 
    {
#if _WIN32
        LPTSTR lpMsgBuf; 
        FormatMessage( 
            FORMAT_MESSAGE_ALLOCATE_BUFFER | 
            FORMAT_MESSAGE_FROM_SYSTEM | 
            FORMAT_MESSAGE_IGNORE_INSERTS, 
            NULL, 
            GetLastError(), 
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
            (LPTSTR)&lpMsgBuf, 
            0, 
            NULL 
            ); 
        String ret = lpMsgBuf;
        // Free the buffer.
        LocalFree( lpMsgBuf );
        return ret;
#else
        return String(dlerror());
#endif
    }

}
